package org.lemma.set;

import java.util.WeakHashMap;
import javax.annotation.Author;
import javax.annotation.Copyright;
import javax.annotation.Version;
import org.lemma.number.Numeric;

/**
 * <p>
 *  TODO: Javadoc for {@code AbstractRelation}
 * </p>
 *
 * @author Chris Beatty [christopher.beatty@gmail.com]
 * @version 1.0.0
 */
@Version (
    major="1", minor="0", patch="0", 
    date="Nov 4, 2008 1:31:53 PM",
    authors = {
        @Author (name="Chris Beatty", email="christopher.beatty@gmail.com")
    }
) @Copyright 
public abstract class      AbstractRelation<D, C, M extends Numeric>
                implements Relation<D,C,M> {

    /**
     * {@inheritDoc}
     *
     * @param arg {@inheritDoc}
     * @return {@inheritDoc}
     */
    @Override
    public Set<C,M> relate(final D arg) {
        final Relation<D,C,M> r = this;

        return new Set<C,M>() {

            @Override
            public M hasMember(final C element) {
                return r.hasMember(new Tuple<D,C>(arg, element));
            }

        };
    }

    /**
     * {@inheritDoc}
     *
     * @param <C2> {@inheritDoc}
     * @param g {@inheritDoc}
     * @return  {@inheritDoc}
     */
    @Override
    public <C2> Relation<D,C2,M> compose(final Relation<C,C2,M> g) {
        if(g == null) {
            throw new IllegalArgumentException("Cannot compose a NULL");
        }
        final Relation<D,C,M> f = this;

        return new AbstractRelation<D,C2,M>() {

            private WeakHashMap<Tuple<D,C2>, M> cache = new WeakHashMap<Tuple<D,C2>, M>();

            //http://documents.wolfram.com/applications/fuzzylogic/DemonstrationNotebooks/3.html
            @Override
            public M hasMember(final Tuple<D,C2> arg) {
                if(arg == null) {
                    throw new IllegalArgumentException("Tuple is NULL");
                } else if(cache.containsKey(arg)) {
                    return cache.get(arg);
                } else {
                    M result = null;
                    D  d = arg.car();
                    C2 s = arg.cdr();
                    //for(C c : f.relate(d)) {
                    //  M v1 = f.hasMember(new Tuple<D,C>(d,c));
                    //  M v2 = g.hasMember(new Tuple<C,S>(c,s));
                    //  result = (result == null) ? min(v1,v2) : max(result, min(v1, v2));
                    //}
                    cache.put(arg, result);
                    return result;
                }
            }

        };
    }

}
